/*
Kakine
ActionScript Framework
Copyright © 2011, PUNY, LLC.

This file is part of KAKINE.

KAKINE is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

KAKINE is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with KAKINE.  If not, see <http://www.gnu.org/licenses/>.
*/ 
package com.puny.mvc.controllers.platformer
{
	import com.puny.mvc.views.View;
	import com.puny.mvc.views.platformer.ItemView;
	
	import flash.display.DisplayObject;
	import flash.geom.Point;

	/**
	 * $Revision: 592 $
	 */
	public class CharacterController extends VerletController
	{
		private static const NAN:int = -1;
		private static const TOP:int = 0;
		private static const BOTTOM:int = 2;
		private static const LEFT:int = 1;
		private static const RIGHT:int = 3;
		private var _ground:View;
		private var _boundX:Boolean = false;
		private var _boundY:Boolean = false;
		private var _kinetic_friction:Number = 100;
		private var _interactions:Array = new Array();
		protected var _force:Point = new Point();
		
		public function CharacterController(filename:String=null) {
			super(filename);
		}
		
		public function set boundX(value:Boolean):void {
			_boundX = value;
		}
		
		public function set boundY(value:Boolean):void {
			_boundY = value;
		}
		
		public function set kineticFriction(value:Number):void {
			_kinetic_friction = value;
		}		
		
		public function hit(target:CharacterController):Boolean {return false;}

		public function isOnGround():Boolean {
			return _ground != null;
		}
		
		public function isFallingDown():Boolean {
			return !isOnGround();
		}
		
		public function get groundItem():View {
			return _ground;
		}
		
		public function get force():Point {
			return _force;
		}
		
		public function move(gravity:Point):void {
			fallingDown(gravity.add(_force));
//			_force.x = isOnGround() ? _kinetic_friction*(traceX()-x) : 0;
			_force.x = _kinetic_friction*(traceX()-x);
			_force.y = 0;
			_ground = null;
		}
		
		public function addInteraction(char:CharacterController):void {
			_interactions.push(char);
		}
		
		public function get interactions():Array {
			return _interactions;
		}
		
		protected function collisionAtWall(item:View):void { }

		protected function fallingDown(gravity:Point):void {
			x = verlet(traceX(),x,gravity.x);
			y = verlet(traceY(),y,gravity.y);
		}
		
		public function collisionPoint(vectors:Array):int {
			var vx:Number = x - traceX();
			var vy:Number = y - traceY();
			if ( inArea(vx,vy,vectors[0],vectors[1]) ) {
				return TOP;
			} else if (inArea(vx,vy,vectors[1],vectors[2])) {
				return RIGHT;
			} else if (inArea(vx,vy,vectors[2],vectors[3])) {
				return BOTTOM;
			} else if (inArea(vx,vy,vectors[3],vectors[0])) {
				return LEFT;
			}
			return NAN;
		}
				
		public function adjustMovement(gravity:Point, item:ItemView):void {
			var collisionFrom:int = collisionPoint(item.getCollisionVector(this));
			var diff:Number;
			var tmp:Number;

			if (gravity.y >= 0) {
				tmp = y;
				if (collisionFrom == BOTTOM) {
					if (!item.canThru) {
						_boundY ? boundYAt(item.y + item.height) : stopYAt(item.y + item.height);
					}
				} else if (collisionFrom == TOP && item.y + item.height > y) {
					_ground = item;
					_boundY ? boundYAt(item.y - height) : stopYAt(item.y - height);
				}
			} else {
				tmp = y;
				if (collisionFrom == TOP) {
					_boundY ? boundYAt(item.y - height) : stopYAt(item.y - height);
				} else if (collisionFrom == BOTTOM && item.y < y+height) {
					_ground = item;
					_boundY ? boundYAt(item.y + item.height) : stopYAt(item.y + item.height);
				}
			}
			if (!item.canThru) {
				if (y - item.height < item.y && item.y < y + height - 1) {
					tmp = x;
					if (collisionFrom == LEFT) {
						collisionAtWall(item);
						_boundX ? boundXAt( item.x - width ) : stopXAt( item.x - width -1);
					} else if (collisionFrom == RIGHT) {
						collisionAtWall(item);
						_boundX ? boundXAt( item.x + item.width ) :	stopXAt( item.x + item.width +1);
					}
				}
			}
		}
		
		override public function hitTestObject(obj:DisplayObject):Boolean {
//			var hit:Boolean = super.hitTestObject(obj);
			var hit:Boolean = (obj.x < x+width) &&
							  (x < obj.x + obj.width) &&
							  (obj.y < y+height) &&
							  (y < obj.y + obj.height);
			var item:ItemView;
			if (!hit && obj is ItemView) {
				item = obj as ItemView;
				// Avoid to go through a wall.
				if (y + height > item.y && y < item.y + item.height) {
					if ( traceX() + width < item.x && item.x + item.width < x ) {
						x = traceX();
						x = item.x + item.width /2;
						return true;
					} else if (x + width < item.x && item.x + item.width < traceX()) {
						x = traceX();
						x = item.x + item.width /2;
						return true;
					}
				}
				// Avoid to go through a floor or ceiling.
				if (x + width > item.x && x < item.x + item.width) {
					if ( traceY() + height < item.y && item.y + item.height < y) {
						//Something to be done here.
						return true;
					} else if (y + height < item.y && item.y + item.height < traceY()) {
						//Something to be done here.
						return true;
					}
				}
			}
			return hit;
		}
		
		private function inArea(vx:Number,vy:Number,a:Point,b:Point):Boolean {
			var d:Number = a.x * b.y - b.x * a.y;
			if (d == 0) {
				return false;
			}
			var t:Number = (b.y * vx - b.x * vy) / d;
			var s:Number = (-a.y * vx + a.x * vy) / d;
			return t > 0 && s > 0;
		}
		
	}
}